﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Net;
using System.Xml.Linq;
using System.Xml.Serialization;
using System;
using System.Threading;
using AgiliTrain.PhoneyTools;

namespace GooNews.ViewModels
{
  /// <summary>
  /// 
  /// </summary>
  public class OptionsModel : ObservableObject
  {
    ObservableCollection<FeedInfo> _searchFeedTypes;
    ObservableCollection<FeedInfo> _builtInFeedTypes;

    /// <summary>
    /// Initializes a new instance of the <see cref="OptionsModel"/> class.
    /// </summary>
    public OptionsModel()
    {
    }

    const string OPTIONSPATH = "options.xml";
    const string NEDSPATH = "neds.xml";

    /// <summary>
    /// Saves this instance.
    /// </summary>
    public void Save()
    {
      using (var store = IsolatedStorageFile.GetUserStoreForApplication())
      using (var cache = store.CreateFile(OPTIONSPATH))
      {
        var ser = new XmlSerializer(typeof(OptionsModel));
        ser.Serialize(cache, this);
      }

    }

    /// <summary>
    /// Loads this instance.
    /// </summary>
    /// <returns></returns>
    public static OptionsModel Load()
    {

      using (var store = IsolatedStorageFile.GetUserStoreForApplication())
      {
        if (store.FileExists(OPTIONSPATH))
        {
          using (var cache = store.OpenFile(OPTIONSPATH, FileMode.Open))
          {
            try
            {
              var ser = new XmlSerializer(typeof(OptionsModel));
              var results = (OptionsModel)ser.Deserialize(cache);
              return results;
            }
            catch
            {
              // NOOP
            }
          }
        }
      }

      // If file doesn't exist or serialization fails,
      // recreate the options
      return ResetOptions();

    }

    /// <summary>
    /// Resets the options.
    /// </summary>
    /// <returns></returns>
    public static OptionsModel ResetOptions()
    {
      using (var store = IsolatedStorageFile.GetUserStoreForApplication())
      {
        if (store.FileExists(OPTIONSPATH))
        {
          store.DeleteFile(OPTIONSPATH);
        }
      }

      var options = new OptionsModel();
      options.AddDefaults();

      return options;
    }


    /// <summary>
    /// Builds the default feed info.
    /// </summary>
    /// <param name="topic">The topic.</param>
    /// <param name="title">The title.</param>
    /// <returns></returns>
    FeedInfo BuildDefaultFeedInfo(string topic, string title)
    {
      return new FeedInfo()
         {
           Title = title,
           Type = FeedInfo.FeedType.BuiltIn,
           Topic = topic
         };
    }

    /// <summary>
    /// Adds the defaults.
    /// </summary>
    void AddDefaults()
    {
      SearchFeedTypes = new ObservableCollection<FeedInfo>();
      RebuildDefaultFeeds();
    }

    /// <summary>
    /// Rebuilds the default feeds.
    /// </summary>
    void RebuildDefaultFeeds()
    {
      BuiltInFeedTypes = new ObservableCollection<FeedInfo>();

      var ned = LoadNed();

      foreach (var item in ned.Topics)
      {
        BuiltInFeedTypes.Add(BuildDefaultFeedInfo(item.Key, item.Value));
      }
    }

    /// <summary>
    /// Gets or sets the search feed types.
    /// </summary>
    /// <value>
    /// The search feed types.
    /// </value>
    public ObservableCollection<FeedInfo> SearchFeedTypes
    {
      get { return _searchFeedTypes; }
      set
      {
        _searchFeedTypes = value;
        RaisePropertyChanged("SearchFeedTypes");
      }
    }

    /// <summary>
    /// Adds the search feed info.
    /// </summary>
    /// <param name="title">The title.</param>
    public void AddSearchFeedInfo(string title)
    {
      SearchFeedTypes.Add(new FeedInfo()
        {
          Title = title,
          Topic = HttpUtility.UrlEncode(title.ToLower()),
          Type = FeedInfo.FeedType.Search,
          Enabled = true
        });
    }

    /// <summary>
    /// Gets or sets the built in feed types.
    /// </summary>
    /// <value>
    /// The built in feed types.
    /// </value>
    public ObservableCollection<FeedInfo> BuiltInFeedTypes
    {
      get
      {
        return _builtInFeedTypes;
      }
      set
      {
        _builtInFeedTypes = value;
        RaisePropertyChanged("BuiltInFeedTypes");
      }
    }

    // https://www.instapaper.com/api/add?username=shawnwildermuth&password=vernyay&url=http://wildermuth.com

    /// <summary>
    /// Gets a value indicating whether [allow insta paper].
    /// </summary>
    /// <value>
    ///   <c>true</c> if [allow insta paper]; otherwise, <c>false</c>.
    /// </value>
    [XmlIgnore]
    public bool AllowInstaPaper
    {
      get { return !string.IsNullOrEmpty(InstaPaperUserName); }
    }

    string _instaPaperUserName;

    /// <summary>
    /// Gets or sets the name of the insta paper user.
    /// </summary>
    /// <value>
    /// The name of the insta paper user.
    /// </value>
    public string InstaPaperUserName
    {
      get { return _instaPaperUserName; }
      set
      {
        _instaPaperUserName = value;
        RaisePropertyChanged("InstaPaperUserName");
        RaisePropertyChanged("AllowInstaPaper");
      }
    }

    string _instaPaperPassword;

    /// <summary>
    /// Gets or sets the insta paper password.
    /// </summary>
    /// <value>
    /// The insta paper password.
    /// </value>
    public string InstaPaperPassword
    {
      get { return _instaPaperPassword; }
      set
      {
        _instaPaperPassword = value;
        RaisePropertyChanged("InstaPaperPassword");
        RaisePropertyChanged("AllowInstaPaper");
      }
    }

    /// <summary>
    /// Gets the feed types.
    /// </summary>
    [XmlIgnore]
    public IEnumerable<FeedInfo> FeedTypes
    {
      get
      {
        return BuiltInFeedTypes.Where(f => f.Enabled).Union(SearchFeedTypes);
      }
    }

    string _currentNedKey = "us";

    /// <summary>
    /// Gets or sets the current ned key.
    /// </summary>
    /// <value>
    /// The current ned key.
    /// </value>
    public string CurrentNedKey
    {
      get { return _currentNedKey; }
      set
      {
        _currentNedKey = value;
        RaisePropertyChanged("CurrentNedKey");
      }
    }

    List<Ned> _neds = null;

    /// <summary>
    /// Gets the neds.
    /// </summary>
    [XmlIgnore]
    public List<Ned> Neds
    {
      get
      {
        if (_neds == null)
        {
          ThreadPool.QueueUserWorkItem(new WaitCallback(o =>
            {
              _neds = LoadNedList();
              RaisePropertyChanged("Neds");
              RaisePropertyChanged("CurrentNed");
            }));
#if DEBUG
          //_neds.ForEach(n => Debug.WriteLine(n.Name));
#endif
        }
        return _neds;
      }
    }

    /// <summary>
    /// Gets or sets the current ned.
    /// </summary>
    /// <value>
    /// The current ned.
    /// </value>
    [XmlIgnore]
    public Ned CurrentNed
    {
      get
      {
        if (Neds == null) return null;
        return Neds.Where(n => n.Key == _currentNedKey).FirstOrDefault();
      }
      set
      {
        CurrentNedKey = value.Key;
        RebuildDefaultFeeds();
      }
    }

    /// <summary>
    /// Loads the ned list.
    /// </summary>
    /// <returns></returns>
    List<Ned> LoadNedList()
    {
      try
      {
        var doc = XDocument.Load(NEDSPATH);

        return doc.Descendants("ned")
                  .OrderBy(ele => ele.Attribute("name").Value)
                  .Select(ele =>
                    new Ned()
                    {
                      Name = ele.Attribute("name").Value,
                      Key = ele.Attribute("id").Value
                    }).ToList();
      }
      catch
#if DEBUG
        (Exception ex)
#endif
      {
        return null;
      }
    }



    /// <summary>
    /// Loads the ned.
    /// </summary>
    /// <returns></returns>
    Ned LoadNed()
    {
      var doc = XDocument.Load(NEDSPATH);

      var node = doc.Descendants("ned")
                    .Where(n => n.Attribute("id").Value == _currentNedKey)
                    .FirstOrDefault();

      if (node != null)
      {
        var theNed = new Ned()
        {
          Name = node.Attribute("name").Value,
          Key = node.Attribute("id").Value,
          Topics = new Dictionary<string, string>()
        };

        foreach (var t in node.Descendants())
        {
          theNed.Topics.Add(t.Attribute("key").Value, t.Value);
        }

        return theNed;
      }

      return null;
    }

    static double[] sizes = { 12, 15, 18, 20, 22, 24, 26, 30, 36 };

    /// <summary>
    /// Changes the size of the font.
    /// </summary>
    /// <param name="up">if set to <c>true</c> [up].</param>
    public void ChangeFontSize(bool up)
    {
      var index = Array.IndexOf<double>(sizes, _storyFontSize);

      // Determine next index
      if (up) index++;
      else index--;
      index = Math.Min(sizes.Count() - 1, index);
      index = Math.Max(0, index);

      StoryFontSize = sizes[index];
    }

    double _storyFontSize = 24;

    /// <summary>
    /// Gets or sets the size of the story font.
    /// </summary>
    /// <value>
    /// The size of the story font.
    /// </value>
    public double StoryFontSize
    {
      get { return _storyFontSize; }
      set
      {
        _storyFontSize = value;
        RaisePropertyChanged("StoryFontSize");
      }
    }

    string _favoriteFeed;

    /// <summary>
    /// Gets or sets the favorite feed.
    /// </summary>
    /// <value>
    /// The favorite feed.
    /// </value>
    public string FavoriteFeed
    {
      get { return _favoriteFeed; }
      set
      {
        _favoriteFeed = value; ;
      }
    }
  }
}
